#include <vector>

#include "gsupplyLinkSolution.h"

using namespace std ;

GSupplyLinkProblem* globalProblem ;

GSupplyLinkSolution::GSupplyLinkSolution():problem(globalProblem),evaluation(-1){
    if (globalProblem!=NULL && globalProblem->getNP()>0)
        processingSchedule.resize(globalProblem->getNP()) ;
}

GSupplyLinkSolution::GSupplyLinkSolution(GSupplyLinkProblem& pb_):problem(&pb_),processingSchedule(pb_.getNP()),evaluation(-1){
    globalProblem = problem ;
}

double GSupplyLinkSolution::evaluate() {
    evaluation = -1 ;
    if (!checkConstraints()) return -1 ;
    evaluationTransporter = nbrBatch * problem->getTransporter(0).getDeliveryCost() ;
    evaluationCustomer = 0 ;
    for (int i=0;i<problem->getNP();++i) {
        double jobcustcost = problem->getJobHoldingCost(i) ;
        double jobduedate = problem->getJobDueDate(i) ;
        double jobcomptime =  processingSchedule[i].getDeliveryCompletionTime() ;
        evaluationCustomer += jobcustcost*(jobduedate-jobcomptime) ;
    }
    evaluation = evaluationTransporter + evaluationCustomer ;
    return evaluation;
}

/**
 * checks if the solution satisfies the constraints
 *
 * @return true if the constraints are satisfied
 */
bool GSupplyLinkSolution::checkConstraints() {
    //get the number of batches ;
    int nbrbatch = 1 ;
    for(int i=0; i<problem->getNP(); i++){
        if (nbrbatch<processingSchedule[i].getBatchIndice()){
            nbrbatch = processingSchedule[i].getBatchIndice() ;
        }
    }
    this->nbrBatch = nbrbatch ;

    assert (this->nbrBatch>0) ;

    vector<BatchInformation> tabBatchInformation(this->nbrBatch) ;

    for (int i=0;i<problem->getNP();++i) {
        int batchIndice = processingSchedule[i].getBatchIndice() - 1 ;

        assert (batchIndice>=0 & batchIndice<this->nbrBatch) ;

        // Constraint (3) : Add the size of job i to the batch where job is is assigned
        tabBatchInformation[batchIndice].size += problem->getJobSize(i) ;

        // Constraint (2) : Update the completion time of the batch if the due date of the job is less
        if (tabBatchInformation[batchIndice].completionTime > problem->getJobDueDate(i)) {
            tabBatchInformation[batchIndice].completionTime = problem->getJobDueDate(i) ;
        }
    }

    double lastDeliveryDate = PLUS_INFINI ;
    for (int b=this->nbrBatch-1;b>=0;--b) {
        // Constraint (3) : capacity constraint check
        assert (tabBatchInformation[b].size <= problem->getTransporter(0).getCapacity()) ;

        // Constraint (5) : the interval between two successive delivery should be greater or equal to the delivery time
        if (tabBatchInformation[b].completionTime > lastDeliveryDate-problem->getTransporter(0).getDeliveryTime()) {
            tabBatchInformation[b].completionTime = lastDeliveryDate-problem->getTransporter(0).getDeliveryTime() ;
        }
        lastDeliveryDate = tabBatchInformation[b].completionTime ;

    }

    // Constraint (4) : check if all th jobs in a batch have the same completion time as the batch
    for (int i=0;i<problem->getNP();++i) {
        int batchnumber = processingSchedule[i].getBatchIndice()-1 ;

        // Constraint (2) : Update the completion time of the batch if the due date of the job is less
        if (tabBatchInformation[batchnumber].completionTime != processingSchedule[i].getDeliveryCompletionTime()) {
            processingSchedule[i].setDeliveryCompletionTime (tabBatchInformation[batchnumber].completionTime) ;
        }
    }

    return true;
}

/**
 *
 */
ostream& operator<< (ostream& o, const GSupplyLinkSolution& sol) {
    /*
        int nbrBatch;
        vector<GJobVariable> processingSchedule;
        double evaluation ;
        double evaluationTransporter;
        double evaluationCustomer;
    */
    o << "GSupplyLinkSolution[[nbrBatch=" << sol.nbrBatch << ", evaluation=" << sol.evaluation ;
    o << ", evaluationCustomer=" << sol.evaluationCustomer << ", evaluationTransporter=" << sol.evaluationTransporter  ;
    o << "\n  bi=[" ;
    int n = sol.problem->getNP() ;
    for (int i=0;i<n;++i) {
      o << sol.processingSchedule[i].getBatchIndice() ;
      if (i<n-1) o << ", "; else o << "]" ;
    }
    o << "\n  Ci=[" ;
    for (int i=0;i<n;i++) {
      o << sol.processingSchedule[i].getDeliveryCompletionTime() ;
      if (i<n-1) o <<  ", "; else o <<  "]" ;
    }
    o << "]" ;

    return o ;
}



